/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package utilities;

import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

/**
 *
 * @author ctuchsch
 */
public abstract class CommonCamera implements Camera
{

    private float fov;
    private float aspectRatio;
    private float zNear;
    private float zFar;
    private float x_scale;
    private float y_scale;
    public Matrix4f projectionMatrix = null;
    public Matrix4f viewMatrix = null;
    private Vector3f cameraAngle = null;
    private Vector3f cameraScale = null;
    private FloatBuffer matrix44Buffer = null;
    private float frustum_length;
    private Vector3f cameraPos;

    @Override
    public void applyTranslations()
    {
        viewMatrix = new Matrix4f();
        // Matrix4f.scale(cameraScale, viewMatrix, viewMatrix);      
        Matrix4f.rotate(Util.degreesToRadians(cameraAngle.x), new Vector3f(1, 0, 0), viewMatrix, viewMatrix);
        Matrix4f.rotate(Util.degreesToRadians(cameraAngle.y), new Vector3f(0, 1, 0), viewMatrix, viewMatrix);
        Matrix4f.rotate(Util.degreesToRadians(cameraAngle.z), new Vector3f(0, 0, 1), viewMatrix, viewMatrix);
        Matrix4f.translate(cameraPos, viewMatrix, viewMatrix);
    }

    @Override
    public void setPosition(float x, float y, float z)
    {
        cameraPos.x = x;
        cameraPos.y = y;
        cameraPos.z = z;
    }

    @Override
    public void setRotation(float pitch, float yaw, float roll)
    {
        cameraAngle.x = pitch;
        cameraAngle.y = yaw;
        cameraAngle.z = roll;
    }

    public void setX(float x)
    {
        cameraPos.x = x;
    }

    public void setY(float y)
    {
        cameraPos.y = y;
    }

    public void setZ(float z)
    {
        cameraPos.z = z;
    }

    public void setPitch(float pitch)
    {
        cameraAngle.x = pitch;
    }

    public void setRoll(float roll)
    {
        cameraAngle.z = roll;
    }

    public void setYaw(float yaw)
    {
        cameraAngle.y = yaw;
    }

    @Override
    public float pitch()
    {
        return cameraAngle.x;
    }

    @Override
    public float yaw()
    {
        return cameraAngle.y;
    }

    @Override
    public float roll()
    {
        return cameraAngle.z;
    }

    @Override
    public float x()
    {
        return cameraPos.x;
    }

    @Override
    public float y()
    {
        return cameraPos.y;
    }

    @Override
    public float z()
    {
        return cameraPos.z;
    }

    @Override
    public float fieldOfView()
    {
        return fov;
    }

    @Override
    public void setFieldOfView(float fov)
    {
        this.fov = fov;
    }

    @Override
    public void setAspectRatio(float aspectRatio)
    {
        if (aspectRatio <= 0)
            throw new IllegalArgumentException("aspectRatio " + aspectRatio + " is 0 or less");
        this.aspectRatio = aspectRatio;
    }

    @Override
    public float aspectRatio()
    {
        return aspectRatio;
    }

    @Override
    public float nearClippingPane()
    {
        return zNear;
    }

    @Override
    public float farClippingPane()
    {
        return zFar;
    }

    @Override
    public String toString()
    {
        return "EulerCamera [x=" + x() + ", y=" + y() + ", z=" + z() + ", pitch=" + pitch()
                + ", yaw=" + yaw() + ", roll=" + roll() + ", fov=" + fov
                + ", aspectRatio=" + aspectRatio + ", zNear=" + zNear
                + ", zFar=" + zFar + "]";
    }

    public CommonCamera(float aspectRatio, float x, float y, float z, float pitch, float yaw, float roll, float zNear, float zFar, float fov)
    {
        if (aspectRatio <= 0)
            throw new IllegalArgumentException("aspectRatio " + aspectRatio + " was 0 or was smaller than 0");
        if (zNear <= 0)
            throw new IllegalArgumentException("zNear " + zNear + " was 0 or was smaller than 0");
        if (zFar <= zNear)
            throw new IllegalArgumentException("zFar " + zFar + " was smaller or the same as zNear " + zNear);
        this.aspectRatio = aspectRatio;
        this.cameraPos = new Vector3f(-x, -y, -z);
        this.cameraAngle = new Vector3f(-pitch, -yaw, -roll);
        this.zNear = zNear;
        this.zFar = zFar;
        this.fov = fov;
        this.y_scale = Util.coTangent(Util.degreesToRadians(fov / 2f));
        this.x_scale = y_scale / aspectRatio;
        this.frustum_length = zFar - zNear;
        setUpMatrices();
    }

    private void setUpMatrices()
    {
        projectionMatrix = new Matrix4f();
        projectionMatrix.m00 = x_scale;
        projectionMatrix.m11 = y_scale;
        projectionMatrix.m22 = -((zFar + zNear) / frustum_length);
        projectionMatrix.m23 = -1;
        projectionMatrix.m32 = -((2 * zNear * zFar) / frustum_length);

        viewMatrix = new Matrix4f();

        matrix44Buffer = BufferUtils.createFloatBuffer(16);        
    }

    @Override
    public FloatBuffer getProjectionMatrix()
    {
        projectionMatrix.store(matrix44Buffer);
        matrix44Buffer.flip();
        return matrix44Buffer;
    }

    @Override
    public FloatBuffer getViewMatrix()
    {
        viewMatrix.store(matrix44Buffer);
        matrix44Buffer.flip();
        return matrix44Buffer;
    }
}
